unit DIrr.Vector2D;

{$I DIrr.inc}

interface

uses
  Math, DIrr.Math;

type
  PVector2DF = ^TVector2DF;
  TVector2DF = record
    X, Y: Single;
  public
    procedure Init(nx, ny: Single); inline;
    constructor Create(nx, ny: Single);
  public
    class operator Negative(Value: TVector2DF): TVector2DF;
    class operator Inc(Value: TVector2DF): TVector2DF;
    class operator Dec(Value: TVector2DF): TVector2DF;
    class operator Equal(Value1, Value2: TVector2DF): Boolean;
    class operator NotEqual(Value1, Value2: TVector2DF): Boolean;
    class operator GreaterThan(Value1, Value2: TVector2DF): Boolean;
    class operator GreaterThanOrEqual(Value1, Value2: TVector2DF): Boolean;
    class operator LessThan(Value1, Value2: TVector2DF): Boolean;
    class operator LessThanOrEqual(Value1, Value2: TVector2DF): Boolean;
    class operator Add(Value1, Value2: TVector2DF): TVector2DF;
    class operator Subtract(Value1, Value2: TVector2DF): TVector2DF;
    class operator Multiply(Value1, Value2: TVector2DF): TVector2DF;
    class operator Multiply(Value1: TVector2DF; Value2: Single): TVector2DF;
    class operator Divide(Value1, Value2: TVector2DF): TVector2DF;
    class operator Divide(Value1: TVector2DF; Value2: Single): TVector2DF;

    procedure SetValue(nx, ny: Single); overload; {$IFDEF INLINE} inline; {$ENDIF}
    procedure SetValue(nv: TVector2DF); overload; {$IFDEF INLINE} inline; {$ENDIF}

	  //! Gets the length of the vector.
	  (* \return The length of the vector. *)
    function GetLength: Single;
	  //! Get the squared length of this vector
	  (* This is useful because it is much faster than getLength().
	  \return The squared length of the vector. *)
    function GetSquaredLength: Single;
	  //! Get the dot product of this vector with another.
	  (* \param other Other vector to take dot product with.
	  \return The dot product of the two vectors. *)
    function DotProduct(Value: TVector2DF): Single;
    //! Gets distance from another point.
    (* Here, the vector is interpreted as a point in 2-dimensional space.
    \param other Other vector to measure from.
    \return Distance from other point. *)
    function DistanceFrom(Value: TVector2DF): Single;
    //! Returns squared distance from another point.
    (* Here, the vector is interpreted as a point in 2-dimensional space.
    \param other Other vector to measure from.
    \return Squared distance from other point. *)
    function SquaredDistanceFrom(Value: TVector2DF): Single;
    //! rotates the point anticlockwise around a center by an amount of degrees.
    (* \param degrees Amount of degrees to rotate by, anticlockwise.
    \param center Rotation center.
    \return This vector after transformation. *)
    function RotateBy(Degree: Double; Center: TVector2DF): TVector2DF; overload;
    function RotateBy(Degree: Double): TVector2DF; overload;
	  //! Normalize the vector.
	  (* The null vector is left untouched.
	  \return Reference to this vector, after normalization. *)
    function Normalize: TVector2DF;
    //! Calculates the angle of this vector in degrees in the trigonometric sense.
    (* 0 is to the right (3 o'clock), values increase counter-clockwise.
    This method has been suggested by Pr3t3nd3r.
    \return Returns a value between 0 and 360. *)
    function GetAngleTrig: Double;
    //! Calculates the angle of this vector in degrees in the counter trigonometric sense.
    (* 0 is to the right (3 o'clock), values increase clockwise.
    \return Returns a value between 0 and 360. *)
    function GetAngle: Double;
    //! Calculates the angle between this vector and another one in degree.
    (* \param b Other vector to test with.
    \return Returns a value between 0 and 90. *)
    function GetAngleWith(const b: TVector2DF): Double;
    //! Returns if this vector interpreted as a point is on a line between two other points.
    (* It is assumed that the point is on the line.
    \param begin Beginning vector to compare between.
    \param end Ending vector to compare between.
    \return True if this vector is between begin and end, false if not. *)
    function IsBetweenPoints(const BeginP, EndP: TVector2DF): Boolean;
    //! Creates an interpolated vector between this vector and another vector.
    (* \param other The other vector to interpolate with.
    \param d Interpolation value between 0.0f (all the other vector) and 1.0f (all this vector).
    Note that this is the opposite direction of interpolation to getInterpolated_quadratic()
    \return An interpolated vector.  This vector is not modified. *)
    function GetInterpolated(const Other: TVector2DF; d: Double): TVector2DF;
    //! Creates a quadratically interpolated vector between this and two other vectors.
    (* \param v2 Second vector to interpolate with.
    \param v3 Third vector to interpolate with (maximum at 1.0f)
    \param d Interpolation value between 0.0f (all this vector) and 1.0f (all the 3rd vector).
    Note that this is the opposite direction of interpolation to getInterpolated() and interpolate()
    \return An interpolated vector. This vector is not modified. *)
    function GetInterpolatedQuadratic(const V2, V3: TVector2DF; d: Double): TVector2DF;
    //! Sets this vector to the linearly interpolated vector between a and b.
    (* \param a first vector to interpolate with, maximum at 1.0f
    \param b second vector to interpolate with, maximum at 0.0f
    \param d Interpolation value between 0.0f (all vector b) and 1.0f (all vector a)
    Note that this is the opposite direction of interpolation to getInterpolated_quadratic()
    *)
    function Interpolate(const a, b: TVector2DF; d: Double): TVector2DF;
  end;

type
  PVector2D = ^TVector2D;
  TVector2D = record
    X, Y: Integer;
  public
    procedure Init(Ax, Ay: Integer); inline;
    constructor Create(Ax, Ay: Integer);

    class operator Negative(Value: TVector2D): TVector2D;
    class operator Inc(Value: TVector2D): TVector2D;
    class operator Dec(Value: TVector2D): TVector2D;
    class operator Equal(Value1, Value2: TVector2D): Boolean;
    class operator NotEqual(Value1, Value2: TVector2D): Boolean;
    class operator GreaterThan(Value1, Value2: TVector2D): Boolean;
    class operator GreaterThanOrEqual(Value1, Value2: TVector2D): Boolean;
    class operator LessThan(Value1, Value2: TVector2D): Boolean;
    class operator LessThanOrEqual(Value1, Value2: TVector2D): Boolean;
    class operator Add(Value1, Value2: TVector2D): TVector2D;
    class operator Subtract(Value1, Value2: TVector2D): TVector2D;
    class operator Multiply(Value1, Value2: TVector2D): TVector2D;
    class operator Multiply(Value1: TVector2D; Value2: Single): TVector2D;
    class operator Divide(Value1, Value2: TVector2D): TVector2D;
    class operator Divide(Value1: TVector2D; Value2: Single): TVector2D;

	  //! Gets the length of the vector.
	  (* \return The length of the vector. *)
    function GetLength: Single;
	  //! Get the squared length of this vector
	  (* This is useful because it is much faster than getLength().
	  \return The squared length of the vector. *)
    function GetSquaredLength: Single;
	  //! Get the dot product of this vector with another.
	  (* \param other Other vector to take dot product with.
	  \return The dot product of the two vectors. *)
    function DotProduct(Value: TVector2D): Single;
    //! Gets distance from another point.
    (* Here, the vector is interpreted as a point in 2-dimensional space.
    \param other Other vector to measure from.
    \return Distance from other point. *)
    function DistanceFrom(Value: TVector2D): Single;
    //! Returns squared distance from another point.
    (* Here, the vector is interpreted as a point in 2-dimensional space.
    \param other Other vector to measure from.
    \return Squared distance from other point. *)
    function SquaredDistanceFrom(Value: TVector2D): Single;
    //! rotates the point anticlockwise around a center by an amount of degrees.
    (* \param degrees Amount of degrees to rotate by, anticlockwise.
    \param center Rotation center.
    \return This vector after transformation. *)
    function RotateBy(Degree: Double; Center: TVector2D): TVector2D; overload;
    function RotateBy(Degree: Double): TVector2D; overload;
	  //! Normalize the vector.
	  (* The null vector is left untouched.
	  \return Reference to this vector, after normalization. *)
    function Normalize: TVector2D;
    //! Calculates the angle of this vector in degrees in the trigonometric sense.
    (* 0 is to the right (3 o'clock), values increase counter-clockwise.
    This method has been suggested by Pr3t3nd3r.
    \return Returns a value between 0 and 360. *)
    function GetAngleTrig: Double;
    //! Calculates the angle of this vector in degrees in the counter trigonometric sense.
    (* 0 is to the right (3 o'clock), values increase clockwise.
    \return Returns a value between 0 and 360. *)
    function GetAngle: Double;
    //! Calculates the angle between this vector and another one in degree.
    (* \param b Other vector to test with.
    \return Returns a value between 0 and 90. *)
    function GetAngleWith(const b: TVector2D): Double;
    //! Returns if this vector interpreted as a point is on a line between two other points.
    (* It is assumed that the point is on the line.
    \param begin Beginning vector to compare between.
    \param end Ending vector to compare between.
    \return True if this vector is between begin and end, false if not. *)
    function IsBetweenPoints(const BeginP, EndP: TVector2D): Boolean;
    //! Creates an interpolated vector between this vector and another vector.
    (* \param other The other vector to interpolate with.
    \param d Interpolation value between 0.0f (all the other vector) and 1.0f (all this vector).
    Note that this is the opposite direction of interpolation to getInterpolated_quadratic()
    \return An interpolated vector.  This vector is not modified. *)
    function GetInterpolated(const Other: TVector2D; d: Double): TVector2D;
    //! Creates a quadratically interpolated vector between this and two other vectors.
    (* \param v2 Second vector to interpolate with.
    \param v3 Third vector to interpolate with (maximum at 1.0f)
    \param d Interpolation value between 0.0f (all this vector) and 1.0f (all the 3rd vector).
    Note that this is the opposite direction of interpolation to getInterpolated() and interpolate()
    \return An interpolated vector. This vector is not modified. *)
    function GetInterpolatedQuadratic(const V2, V3: TVector2D; d: Double): TVector2D;
    //! Sets this vector to the linearly interpolated vector between a and b.
    (* \param a first vector to interpolate with, maximum at 1.0f
    \param b second vector to interpolate with, maximum at 0.0f
    \param d Interpolation value between 0.0f (all vector b) and 1.0f (all vector a)
    Note that this is the opposite direction of interpolation to getInterpolated_quadratic()
    *)
    function Interpolate(const a, b: TVector2D; d: Double): TVector2D;
  end;

type
  PIrrVector2DU = ^TIrrVector2DU;
  TIrrVector2DU = record
    X, Y: UInt32;
  public
    constructor Create(Ax, Ay: Integer);

    class operator Negative(Value: TIrrVector2DU): TIrrVector2DU;
    class operator Inc(Value: TIrrVector2DU): TIrrVector2DU;
    class operator Dec(Value: TIrrVector2DU): TIrrVector2DU;
    class operator Equal(Value1, Value2: TIrrVector2DU): Boolean;
    class operator NotEqual(Value1, Value2: TIrrVector2DU): Boolean;
    class operator GreaterThan(Value1, Value2: TIrrVector2DU): Boolean;
    class operator GreaterThanOrEqual(Value1, Value2: TIrrVector2DU): Boolean;
    class operator LessThan(Value1, Value2: TIrrVector2DU): Boolean;
    class operator LessThanOrEqual(Value1, Value2: TIrrVector2DU): Boolean;
    class operator Add(Value1, Value2: TIrrVector2DU): TIrrVector2DU;
    class operator Subtract(Value1, Value2: TIrrVector2DU): TIrrVector2DU;
    class operator Multiply(Value1, Value2: TIrrVector2DU): TIrrVector2DU;
    class operator Multiply(Value1: TIrrVector2DU; Value2: Single): TIrrVector2DU;
    class operator Divide(Value1, Value2: TIrrVector2DU): TIrrVector2DU;
    class operator Divide(Value1: TIrrVector2DU; Value2: Single): TIrrVector2DU;

	  //! Gets the length of the vector.
	  (* \return The length of the vector. *)
    function GetLength: Single;
	  //! Get the squared length of this vector
	  (* This is useful because it is much faster than getLength().
	  \return The squared length of the vector. *)
    function GetSquaredLength: Single;
	  //! Get the dot product of this vector with another.
	  (* \param other Other vector to take dot product with.
	  \return The dot product of the two vectors. *)
    function DotProduct(Value: TIrrVector2DU): Single;
    //! Gets distance from another point.
    (* Here, the vector is interpreted as a point in 2-dimensional space.
    \param other Other vector to measure from.
    \return Distance from other point. *)
    function DistanceFrom(Value: TIrrVector2DU): Single;
    //! Returns squared distance from another point.
    (* Here, the vector is interpreted as a point in 2-dimensional space.
    \param other Other vector to measure from.
    \return Squared distance from other point. *)
    function SquaredDistanceFrom(Value: TIrrVector2DU): Single;
    //! rotates the point anticlockwise around a center by an amount of degrees.
    (* \param degrees Amount of degrees to rotate by, anticlockwise.
    \param center Rotation center.
    \return This vector after transformation. *)
    function RotateBy(Degree: Double; Center: TIrrVector2DU): TIrrVector2DU; overload;
    function RotateBy(Degree: Double): TIrrVector2DU; overload;
	  //! Normalize the vector.
	  (* The null vector is left untouched.
	  \return Reference to this vector, after normalization. *)
    function Normalize: TIrrVector2DU;
    //! Calculates the angle of this vector in degrees in the trigonometric sense.
    (* 0 is to the right (3 o'clock), values increase counter-clockwise.
    This method has been suggested by Pr3t3nd3r.
    \return Returns a value between 0 and 360. *)
    function GetAngleTrig: Double;
    //! Calculates the angle of this vector in degrees in the counter trigonometric sense.
    (* 0 is to the right (3 o'clock), values increase clockwise.
    \return Returns a value between 0 and 360. *)
    function GetAngle: Double;
    //! Calculates the angle between this vector and another one in degree.
    (* \param b Other vector to test with.
    \return Returns a value between 0 and 90. *)
    function GetAngleWith(const b: TIrrVector2DU): Double;
    //! Returns if this vector interpreted as a point is on a line between two other points.
    (* It is assumed that the point is on the line.
    \param begin Beginning vector to compare between.
    \param end Ending vector to compare between.
    \return True if this vector is between begin and end, false if not. *)
    function IsBetweenPoints(const BeginP, EndP: TIrrVector2DU): Boolean;
    //! Creates an interpolated vector between this vector and another vector.
    (* \param other The other vector to interpolate with.
    \param d Interpolation value between 0.0f (all the other vector) and 1.0f (all this vector).
    Note that this is the opposite direction of interpolation to getInterpolated_quadratic()
    \return An interpolated vector.  This vector is not modified. *)
    function GetInterpolated(const Other: TIrrVector2DU; d: Double): TIrrVector2DU;
    //! Creates a quadratically interpolated vector between this and two other vectors.
    (* \param v2 Second vector to interpolate with.
    \param v3 Third vector to interpolate with (maximum at 1.0f)
    \param d Interpolation value between 0.0f (all this vector) and 1.0f (all the 3rd vector).
    Note that this is the opposite direction of interpolation to getInterpolated() and interpolate()
    \return An interpolated vector. This vector is not modified. *)
    function GetInterpolatedQuadratic(const V2, V3: TIrrVector2DU; d: Double): TIrrVector2DU;
    //! Sets this vector to the linearly interpolated vector between a and b.
    (* \param a first vector to interpolate with, maximum at 1.0f
    \param b second vector to interpolate with, maximum at 0.0f
    \param d Interpolation value between 0.0f (all vector b) and 1.0f (all vector a)
    Note that this is the opposite direction of interpolation to getInterpolated_quadratic()
    *)
    function Interpolate(const a, b: TIrrVector2DU; d: Double): TIrrVector2DU;
  end;

  function Vector2D(X, Y: Integer): TVector2D; {$IFDEF INLINE} inline; {$ENDIF}
  function Vector2DF(X, Y: Single): TVector2DF; {$IFDEF INLINE} inline; {$ENDIF}

type
  TPosition2D = TVector2D;
  TPosition2DF = TVector2DF;

  function Position2D(X, Y: Integer): TPosition2D; {$IFDEF INLINE} inline; {$ENDIF}
  function Position2DF(X, Y: Single): TPosition2DF; {$IFDEF INLINE} inline; {$ENDIF}

implementation

function Vector2D(X, Y: Integer): TVector2D;
begin
  Result.Init(X, Y);
end;

function Vector2DF(X, Y: Single): TVector2DF;
begin
  Result.Init(X, Y);
end;

function Position2D(X, Y: Integer): TPosition2D;
begin
  Result.Init(X, Y);
end;

function Position2DF(X, Y: Single): TPosition2DF;
begin
  Result.Init(X, Y);
end;

{ TVector2DF }

class operator TVector2DF.Add(Value1, Value2: TVector2DF): TVector2DF;
begin
  Result.X := Value1.X + Value2.X;
  Result.Y := Value1.Y + Value2.Y;
end;

procedure TVector2DF.Init(nx, ny: Single);
begin
  X := nx;
  Y := ny;
end;

constructor TVector2DF.Create(nx, ny: Single);
begin
  Init(nx, ny);
end;

class operator TVector2DF.Dec(Value: TVector2DF): TVector2DF;
begin
  Result.X := Value.X - 1;
  Result.Y := Value.Y - 1;
end;

function TVector2DF.DistanceFrom(Value: TVector2DF): Single;
begin
  Result := (Self - Value).GetLength;
end;

class operator TVector2DF.Divide(Value1: TVector2DF;
  Value2: Single): TVector2DF;
begin
  Result.X := Result.X / Value2;
  Result.Y := Result.Y / Value2;
end;

class operator TVector2DF.Divide(Value1, Value2: TVector2DF): TVector2DF;
begin
  Result.X := Value1.X / Value2.X;
  Result.Y := Value1.Y / Value2.Y;
end;

function TVector2DF.DotProduct(Value: TVector2DF): Single;
begin
  Result := X * Value.X + Y * Value.Y;
end;

class operator TVector2DF.Equal(Value1, Value2: TVector2DF): Boolean;
begin
  Result := SameValue(Value1.X, Value2.X) and
    SameValue(Value1.Y, Value2.Y);
end;

function TVector2DF.GetAngle: Double;
var
  tmp: Double;
begin
  if (Y = 0) then   // corrected thanks to a suggestion by Jox
  begin
    if (X < 0) then
      Result := 180
    else
      Result := 0;
    Exit;
  end
  else
  begin
    if (X = 0) then
    begin
      if (Y < 0) then
        Result := 90
      else
        Result := 270;
      Exit;
    end;
  end;

  // don't use getLength here to avoid precision loss with s32 vectors
  tmp := Y / Sqrt(X * X + Y * Y);
  tmp := ArcTan(Sqrt(1 - tmp * tmp) / tmp) * RADTODEG64;

  Result := tmp;
  if (X > 0) and (Y > 0) then
  begin
    Result := tmp + 270;
  end
  else
  begin
    if (X > 0) and (Y < 0) then
    begin
      Result := tmp + 90
    end
    else
    begin
      if (X < 0) and (Y < 0) then
      begin
        Result := 90 - tmp
      end
      else
      begin
        if (X < 0) and (Y > 0) then
          Result := 270 - tmp;
      end;
    end;
  end;
end;

function TVector2DF.GetAngleTrig: Double;
begin
  if (Y = 0) then
  begin
    if (X < 0) then
      Result := 180
    else
      Result := 0;
    Exit;
  end
  else
  begin
    if (X = 0) then
    begin
      if (Y < 0) then
        Result := 270
      else
        Result := 90;
      Exit;
    end;
  end;

  if (Y > 0) then
  begin
    if (X > 0) then
      Result := ArcTan(Y / X) * RADTODEG64
    else
      Result := 180.0 - ArcTan(Y / (-X)) * RADTODEG64;
  end
  else
  begin
    if (X > 0) then
      Result := 360 - ArcTan((-Y) / X ) * RADTODEG64
    else
      Result := 180 + ArcTan((-Y) / (-X));
  end;

end;

function TVector2DF.GetAngleWith(const b: TVector2DF): Double;
var
  tmp: Double;
begin
  tmp := X * b.X + Y * b.Y;

  if (tmp = 0) then
  begin
    Result := 90;
    Exit;
  end;

  tmp := tmp / Sqrt((X * X + Y * Y) * (b.X * b.X + b.Y * b.Y));
  if (tmp < 0) then
    tmp := -tmp;

  Result := ArcTan(Sqrt(1 - tmp * tmp) / tmp) * RADTODEG64;
end;

function TVector2DF.GetInterpolated(const Other: TVector2DF;
  d: Double): TVector2DF;
var
  inv: Double;
begin
  inv := 1.0 - d;
  Result.X := Other.X * inv + X * d;
  Result.Y := Other.Y * inv + Y * d;
end;

function TVector2DF.GetInterpolatedQuadratic(const V2, V3: TVector2DF;
  d: Double): TVector2DF;
var
  inv, mul0, mul1, mul2: Double;
begin
  // this*(1-d)*(1-d)+2*v2*(1-d)+v3*d*d;

  inv := 1.0 - d;
  mul0 := inv * inv;
  mul1 := 2.0 * d * inv;
  mul2 := d * d;

  Result.X := X * mul0 + V2.X * mul1 + V3.X * mul2;
  Result.Y := Y * mul0 + V2.Y * mul1 + V3.Y * mul2;
end;

class operator TVector2DF.GreaterThan(Value1, Value2: TVector2DF): Boolean;
begin
  Result :=((Value1.X > Value2.X) and
      (not SameValue(Value1.X, Value2.X)))
    or (SameValue(Value1.X, Value2.X) and
      (Value1.Y > Value2.Y) and
      (SameValue(Value1.Y, Value2.Y)));
end;

class operator TVector2DF.GreaterThanOrEqual(Value1, Value2: TVector2DF): Boolean;
begin
  with Value1 do
  begin
  Result := (X > Value2.X) or SameValue(X, Value2.X) or
    (SameValue(X, Value2.X) and ((Y > Value2.Y) or SameValue(Y, Value2.Y)) );
  end;
end;

class operator TVector2DF.Inc(Value: TVector2DF): TVector2DF;
begin
  Result.X := Value.X + 1;
  Result.Y := Value.Y + 1;
end;

function TVector2DF.Interpolate(const a, b: TVector2DF; d: Double): TVector2DF;
begin
  X := b.X + (a.X - b.X) * d;
  Y := b.Y + (a.Y - b.Y) * d;
  Result := Self;
end;

function TVector2DF.IsBetweenPoints(const BeginP, EndP: TVector2DF): Boolean;
begin
  if (BeginP.X <> EndP.X) then
  begin
    Result := ((BeginP.X <= X) and (X <= EndP.X)) or
      ((BeginP.X >= X) and (X >= EndP.X));
  end
  else
  begin
    Result := ((BeginP.Y <= Y) and (Y <= EndP.Y)) or
      ((BeginP.Y >= Y) and (Y >= EndP.Y));
  end;
end;

function TVector2DF.GetLength: Single;
begin
  Result := Sqrt(X * X + Y * Y);
end;

class operator TVector2DF.LessThan(Value1, Value2: TVector2DF): Boolean;
begin
  with Value1 do
  begin
    Result := ((X < Value2.X) and (not SameValue(X, Value2.X))) or
      (SameValue(X, Value2.X) and (Y < Value2.Y) and (not SameValue(Y, Value2.Y)));
  end;
end;

class operator TVector2DF.LessThanOrEqual(Value1, Value2: TVector2DF): Boolean;
begin
  with Value1 do
  begin
    Result := (X < Value2.X) or (SameValue(X, Value2.X)) or
      (SameValue(X, Value2.X) and ((Y < Value2.Y) or SameValue(Y, Value2.Y)));
  end;
end;

class operator TVector2DF.Multiply(Value1: TVector2DF;
  Value2: Single): TVector2DF;
begin
  Result.X := Value1.X * Value2;
  Result.Y := Value1.Y * Value2;
end;

class operator TVector2DF.Multiply(Value1, Value2: TVector2DF): TVector2DF;
begin
  Result.X := Value1.X * Value2.X;
  Result.Y := Value1.Y * Value2.Y;
end;

class operator TVector2DF.Negative(Value: TVector2DF): TVector2DF;
begin
  Result.X := -Value.X;
  Result.Y := -Value.Y;
end;

function TVector2DF.Normalize: TVector2DF;
var
  Len: Double;
begin
  Len := X * X + Y * Y;
  if not SameValue(Len, 0) then
  begin
    Len := ReciprocalSqrt(Len);
    X := X * Len;
    Y := Y * Len;
  end;
  Result := Self;
end;

class operator TVector2DF.NotEqual(Value1, Value2: TVector2DF): Boolean;
begin
  Result := not (Value1 = Value2);
end;

function TVector2DF.RotateBy(Degree: Double): TVector2DF;
var
  cs, sn: Double;
begin
  Degree := Degree * DEGTORAD;
  cs := Cos(Degree);
  sn := Sin(Degree);

  X := X * cs - Y * sn;
  Y := Y * sn + Y * cs;

  Result := Self;
end;

function TVector2DF.RotateBy(Degree: Double; Center: TVector2DF): TVector2DF;
var
  cs, sn: Double;
begin
  Degree := Degree * DEGTORAD;
  cs := Cos(Degree);
  sn := Sin(Degree);

  X := X - Center.X;
  Y := Y - Center.Y;

  X := X * cs - Y * sn;
  Y := X * sn + Y * cs;

  Result := Self;
end;

procedure TVector2DF.SetValue(nx, ny: Single);
begin
  X := nx;
  Y := ny;
end;

procedure TVector2DF.SetValue(nv: TVector2DF);
begin
  SetValue(nv.X, nv.Y);
end;

function TVector2DF.SquaredDistanceFrom(Value: TVector2DF): Single;
begin
  Result := (Self - Value).GetSquaredLength;
end;

function TVector2DF.GetSquaredLength: Single;
begin
  Result := X * X + Y * Y;
end;

class operator TVector2DF.Subtract(Value1, Value2: TVector2DF): TVector2DF;
begin
  Result.X := Value1.X - Value2.X;
  Result.Y := Value1.Y - Value2.Y;
end;

{ TVector2D }

class operator TVector2D.Add(Value1, Value2: TVector2D): TVector2D;
begin
  Result.X := Value1.X + Value2.X;
  Result.Y := Value1.Y + Value2.Y;
end;

procedure TVector2D.Init(Ax, Ay: Integer);
begin
  X := Ax;
  Y := Ay;
end;

constructor TVector2D.Create(Ax, Ay: Integer);
begin
  Init(Ax, Ay);
end;

class operator TVector2D.Dec(Value: TVector2D): TVector2D;
begin
  Result.X := Value.X - 1;
  Result.Y := Value.Y - 1;
end;

function TVector2D.DistanceFrom(Value: TVector2D): Single;
begin
  Result := (Self - Value).GetLength;
end;

class operator TVector2D.Divide(Value1: TVector2D;
  Value2: Single): TVector2D;
begin
  Result.X := Round(Result.X / Value2);
  Result.Y := Round(Result.Y / Value2);
end;

class operator TVector2D.Divide(Value1, Value2: TVector2D): TVector2D;
begin
  Result.X := Round(Value1.X / Value2.X);
  Result.Y := Round(Value1.Y / Value2.Y);
end;

function TVector2D.DotProduct(Value: TVector2D): Single;
begin
  Result := X * Value.X + Y * Value.Y;
end;

class operator TVector2D.Equal(Value1, Value2: TVector2D): Boolean;
begin
  Result := SameValue(Value1.X, Value2.X) and
    SameValue(Value1.Y, Value2.Y);
end;

function TVector2D.GetAngle: Double;
var
  tmp: Double;
begin
  if (Y = 0) then   // corrected thanks to a suggestion by Jox
  begin
    if (X < 0) then
      Result := 180
    else
      Result := 0;
    Exit;
  end
  else
  begin
    if (X = 0) then
    begin
      if (Y < 0) then
        Result := 90
      else
        Result := 270;
      Exit;
    end;
  end;

  // don't use getLength here to avoid precision loss with s32 vectors
  tmp := Y / Sqrt(X * X + Y * Y);
  tmp := ArcTan(Sqrt(1 - tmp * tmp) / tmp) * RADTODEG64;

  Result := tmp;
  if (X > 0) and (Y > 0) then
  begin
    Result := tmp + 270;
  end
  else
  begin
    if (X > 0) and (Y < 0) then
    begin
      Result := tmp + 90
    end
    else
    begin
      if (X < 0) and (Y < 0) then
      begin
        Result := 90 - tmp
      end
      else
      begin
        if (X < 0) and (Y > 0) then
          Result := 270 - tmp;
      end;
    end;
  end;
end;

function TVector2D.GetAngleTrig: Double;
begin
  if (Y = 0) then
  begin
    if (X < 0) then
      Result := 180
    else
      Result := 0;
    Exit;
  end
  else
  begin
    if (X = 0) then
    begin
      if (Y < 0) then
        Result := 270
      else
        Result := 90;
      Exit;
    end;
  end;

  if (Y > 0) then
  begin
    if (X > 0) then
      Result := ArcTan(Y / X) * RADTODEG64
    else
      Result := 180.0 - ArcTan(Y / (-X)) * RADTODEG64;
  end
  else
  begin
    if (X > 0) then
      Result := 360 - ArcTan((-Y) / X ) * RADTODEG64
    else
      Result := 180 + ArcTan((-Y) / (-X));
  end;

end;

function TVector2D.GetAngleWith(const b: TVector2D): Double;
var
  tmp: Double;
begin
  tmp := X * b.X + Y * b.Y;

  if (tmp = 0) then
  begin
    Result := 90;
    Exit;
  end;

  tmp := tmp / Sqrt((X * X + Y * Y) * (b.X * b.X + b.Y * b.Y));
  if (tmp < 0) then
    tmp := -tmp;

  Result := ArcTan(Sqrt(1 - tmp * tmp) / tmp) * RADTODEG64;
end;

function TVector2D.GetInterpolated(const Other: TVector2D;
  d: Double): TVector2D;
var
  inv: Double;
begin
  inv := 1.0 - d;
  Result.X := Round(Other.X * inv + X * d);
  Result.Y := Round(Other.Y * inv + Y * d);
end;

function TVector2D.GetInterpolatedQuadratic(const V2, V3: TVector2D;
  d: Double): TVector2D;
var
  inv, mul0, mul1, mul2: Double;
begin
  // this*(1-d)*(1-d)+2*v2*(1-d)+v3*d*d;

  inv := 1.0 - d;
  mul0 := inv * inv;
  mul1 := 2.0 * d * inv;
  mul2 := d * d;

  Result.X := Round(X * mul0 + V2.X * mul1 + V3.X * mul2);
  Result.Y := Round(Y * mul0 + V2.Y * mul1 + V3.Y * mul2);
end;

class operator TVector2D.GreaterThan(Value1, Value2: TVector2D): Boolean;
begin
  Result :=((Value1.X > Value2.X) and
      (not SameValue(Value1.X, Value2.X)))
    or (SameValue(Value1.X, Value2.X) and
      (Value1.Y > Value2.Y) and
      (SameValue(Value1.Y, Value2.Y)));
end;

class operator TVector2D.GreaterThanOrEqual(Value1, Value2: TVector2D): Boolean;
begin
  with Value1 do
  begin
  Result := (X > Value2.X) or SameValue(X, Value2.X) or
    (SameValue(X, Value2.X) and ((Y > Value2.Y) or SameValue(Y, Value2.Y)) );
  end;
end;

class operator TVector2D.Inc(Value: TVector2D): TVector2D;
begin
  Result.X := Value.X + 1;
  Result.Y := Value.Y + 1;
end;

function TVector2D.Interpolate(const a, b: TVector2D; d: Double): TVector2D;
begin
  X := Round(b.X + (a.X - b.X) * d);
  Y := Round(b.Y + (a.Y - b.Y) * d);
  Result := Self;
end;

function TVector2D.IsBetweenPoints(const BeginP, EndP: TVector2D): Boolean;
begin
  if (BeginP.X <> EndP.X) then
  begin
    Result := ((BeginP.X <= X) and (X <= EndP.X)) or
      ((BeginP.X >= X) and (X >= EndP.X));
  end
  else
  begin
    Result := ((BeginP.Y <= Y) and (Y <= EndP.Y)) or
      ((BeginP.Y >= Y) and (Y >= EndP.Y));
  end;
end;

function TVector2D.GetLength: Single;
begin
  Result := Sqrt(X * X + Y * Y);
end;

class operator TVector2D.LessThan(Value1, Value2: TVector2D): Boolean;
begin
  with Value1 do
  begin
    Result := ((X < Value2.X) and (not SameValue(X, Value2.X))) or
      (SameValue(X, Value2.X) and (Y < Value2.Y) and (not SameValue(Y, Value2.Y)));
  end;
end;

class operator TVector2D.LessThanOrEqual(Value1, Value2: TVector2D): Boolean;
begin
  with Value1 do
  begin
    Result := (X < Value2.X) or (SameValue(X, Value2.X)) or
      (SameValue(X, Value2.X) and ((Y < Value2.Y) or SameValue(Y, Value2.Y)));
  end;
end;

class operator TVector2D.Multiply(Value1: TVector2D;
  Value2: Single): TVector2D;
begin
  Result.X := Round(Value1.X * Value2);
  Result.Y := Round(Value1.Y * Value2);
end;

class operator TVector2D.Multiply(Value1, Value2: TVector2D): TVector2D;
begin
  Result.X := Value1.X * Value2.X;
  Result.Y := Value1.Y * Value2.Y;
end;

class operator TVector2D.Negative(Value: TVector2D): TVector2D;
begin
  Result.X := -Value.X;
  Result.Y := -Value.Y;
end;

function TVector2D.Normalize: TVector2D;
var
  Len: Double;
begin
  Len := X * X + Y * Y;
  if not SameValue(Len, 0) then
  begin
    Len := ReciprocalSqrt(Len);
    X := Round(X * Len);
    Y := Round(Y * Len);
  end;
  Result := Self;
end;

class operator TVector2D.NotEqual(Value1, Value2: TVector2D): Boolean;
begin
  Result := not (Value1 = Value2);
end;

function TVector2D.RotateBy(Degree: Double): TVector2D;
var
  cs, sn: Double;
begin
  Degree := Degree * DEGTORAD;
  cs := Cos(Degree);
  sn := Sin(Degree);

  X := Round(X * cs - Y * sn);
  Y := Round(Y * sn + Y * cs);

  Result := Self;
end;

function TVector2D.RotateBy(Degree: Double; Center: TVector2D): TVector2D;
var
  cs, sn: Double;
begin
  Degree := Degree * DEGTORAD;
  cs := Cos(Degree);
  sn := Sin(Degree);

  X := X - Center.X;
  Y := Y - Center.Y;

  X := Round(X * cs - Y * sn);
  Y := Round(X * sn + Y * cs);

  Result := Self;
end;

function TVector2D.SquaredDistanceFrom(Value: TVector2D): Single;
begin
  Result := (Self - Value).GetSquaredLength;
end;

function TVector2D.GetSquaredLength: Single;
begin
  Result := X * X + Y * Y;
end;

class operator TVector2D.Subtract(Value1, Value2: TVector2D): TVector2D;
begin
  Result.X := Value1.X - Value2.X;
  Result.Y := Value1.Y - Value2.Y;
end;

{ TIrrVector2DU }

class operator TIrrVector2DU.Add(Value1, Value2: TIrrVector2DU): TIrrVector2DU;
begin
  Result.X := Value1.X + Value2.X;
  Result.Y := Value1.Y + Value2.Y;
end;

constructor TIrrVector2DU.Create(Ax, Ay: Integer);
begin
  X := Ax;
  Y := Ay;
end;

class operator TIrrVector2DU.Dec(Value: TIrrVector2DU): TIrrVector2DU;
begin
  Result.X := Value.X - 1;
  Result.Y := Value.Y - 1;
end;

function TIrrVector2DU.DistanceFrom(Value: TIrrVector2DU): Single;
begin
  Result := (Self - Value).GetLength;
end;

class operator TIrrVector2DU.Divide(Value1: TIrrVector2DU;
  Value2: Single): TIrrVector2DU;
begin
  Result.X := Round(Result.X / Value2);
  Result.Y := Round(Result.Y / Value2);
end;

class operator TIrrVector2DU.Divide(Value1, Value2: TIrrVector2DU): TIrrVector2DU;
begin
  Result.X := Round(Value1.X / Value2.X);
  Result.Y := Round(Value1.Y / Value2.Y);
end;

function TIrrVector2DU.DotProduct(Value: TIrrVector2DU): Single;
begin
  Result := X * Value.X + Y * Value.Y;
end;

class operator TIrrVector2DU.Equal(Value1, Value2: TIrrVector2DU): Boolean;
begin
  Result := SameValue(Value1.X, Value2.X) and
    SameValue(Value1.Y, Value2.Y);
end;

function TIrrVector2DU.GetAngle: Double;
var
  tmp: Double;
begin
  if (Y = 0) then   // corrected thanks to a suggestion by Jox
  begin
    Result := 180;
    Exit;
  end
  else
  begin
    if (X = 0) then
    begin
      Result := 270;
      Exit;
    end;
  end;

  // don't use getLength here to avoid precision loss with s32 vectors
  tmp := Y / Sqrt(X * X + Y * Y);
  tmp := ArcTan(Sqrt(1 - tmp * tmp) / tmp) * RADTODEG64;

  Result := tmp + 270;
end;

function TIrrVector2DU.GetAngleTrig: Double;
begin
  Result := 0;
  if (Y = 0) then
  begin
    Result := 0;
    Exit;
  end
  else
  begin
    if (X = 0) then
    begin
      Result := 90;
    end;
  end;

  if (Y > 0) then
  begin
    Result := ArcTan(Y / X) * RADTODEG64
  end
  else
  begin
    if (X > 0) then
      Result := 360 - ArcTan((-Y) / X ) * RADTODEG64
  end;
end;

function TIrrVector2DU.GetAngleWith(const b: TIrrVector2DU): Double;
var
  tmp: Double;
begin
  tmp := X * b.X + Y * b.Y;

  if (tmp = 0) then
  begin
    Result := 90;
    Exit;
  end;

  tmp := tmp / Sqrt((X * X + Y * Y) * (b.X * b.X + b.Y * b.Y));
  if (tmp < 0) then
    tmp := -tmp;

  Result := ArcTan(Sqrt(1 - tmp * tmp) / tmp) * RADTODEG64;
end;

function TIrrVector2DU.GetInterpolated(const Other: TIrrVector2DU;
  d: Double): TIrrVector2DU;
var
  inv: Double;
begin
  inv := 1.0 - d;
  Result.X := Round(Other.X * inv + X * d);
  Result.Y := Round(Other.Y * inv + Y * d);
end;

function TIrrVector2DU.GetInterpolatedQuadratic(const V2, V3: TIrrVector2DU;
  d: Double): TIrrVector2DU;
var
  inv, mul0, mul1, mul2: Double;
begin
  // this*(1-d)*(1-d)+2*v2*(1-d)+v3*d*d;

  inv := 1.0 - d;
  mul0 := inv * inv;
  mul1 := 2.0 * d * inv;
  mul2 := d * d;

  Result.X := Round(X * mul0 + V2.X * mul1 + V3.X * mul2);
  Result.Y := Round(Y * mul0 + V2.Y * mul1 + V3.Y * mul2);
end;

class operator TIrrVector2DU.GreaterThan(Value1, Value2: TIrrVector2DU): Boolean;
begin
  Result :=((Value1.X > Value2.X) and
      (not SameValue(Value1.X, Value2.X)))
    or (SameValue(Value1.X, Value2.X) and
      (Value1.Y > Value2.Y) and
      (SameValue(Value1.Y, Value2.Y)));
end;

class operator TIrrVector2DU.GreaterThanOrEqual(Value1, Value2: TIrrVector2DU): Boolean;
begin
  with Value1 do
  begin
  Result := (X > Value2.X) or SameValue(X, Value2.X) or
    (SameValue(X, Value2.X) and ((Y > Value2.Y) or SameValue(Y, Value2.Y)) );
  end;
end;

class operator TIrrVector2DU.Inc(Value: TIrrVector2DU): TIrrVector2DU;
begin
  Result.X := Value.X + 1;
  Result.Y := Value.Y + 1;
end;

function TIrrVector2DU.Interpolate(const a, b: TIrrVector2DU; d: Double): TIrrVector2DU;
begin
  X := Round(b.X + (a.X - b.X) * d);
  Y := Round(b.Y + (a.Y - b.Y) * d);
  Result := Self;
end;

function TIrrVector2DU.IsBetweenPoints(const BeginP, EndP: TIrrVector2DU): Boolean;
begin
  if (BeginP.X <> EndP.X) then
  begin
    Result := ((BeginP.X <= X) and (X <= EndP.X)) or
      ((BeginP.X >= X) and (X >= EndP.X));
  end
  else
  begin
    Result := ((BeginP.Y <= Y) and (Y <= EndP.Y)) or
      ((BeginP.Y >= Y) and (Y >= EndP.Y));
  end;
end;

function TIrrVector2DU.GetLength: Single;
begin
  Result := Sqrt(X * X + Y * Y);
end;

class operator TIrrVector2DU.LessThan(Value1, Value2: TIrrVector2DU): Boolean;
begin
  with Value1 do
  begin
    Result := ((X < Value2.X) and (not SameValue(X, Value2.X))) or
      (SameValue(X, Value2.X) and (Y < Value2.Y) and (not SameValue(Y, Value2.Y)));
  end;
end;

class operator TIrrVector2DU.LessThanOrEqual(Value1, Value2: TIrrVector2DU): Boolean;
begin
  with Value1 do
  begin
    Result := (X < Value2.X) or (SameValue(X, Value2.X)) or
      (SameValue(X, Value2.X) and ((Y < Value2.Y) or SameValue(Y, Value2.Y)));
  end;
end;

class operator TIrrVector2DU.Multiply(Value1: TIrrVector2DU;
  Value2: Single): TIrrVector2DU;
begin
  Result.X := Round(Value1.X * Value2);
  Result.Y := Round(Value1.Y * Value2);
end;

class operator TIrrVector2DU.Multiply(Value1, Value2: TIrrVector2DU): TIrrVector2DU;
begin
  Result.X := Value1.X * Value2.X;
  Result.Y := Value1.Y * Value2.Y;
end;

class operator TIrrVector2DU.Negative(Value: TIrrVector2DU): TIrrVector2DU;
begin
  Result.X := -Value.X;
  Result.Y := -Value.Y;
end;

function TIrrVector2DU.Normalize: TIrrVector2DU;
var
  Len: Double;
begin
  Len := X * X + Y * Y;
  if not SameValue(Len, 0) then
  begin
    Len := ReciprocalSqrt(Len);
    X := Round(X * Len);
    Y := Round(Y * Len);
  end;
  Result := Self;
end;

class operator TIrrVector2DU.NotEqual(Value1, Value2: TIrrVector2DU): Boolean;
begin
  Result := not (Value1 = Value2);
end;

function TIrrVector2DU.RotateBy(Degree: Double): TIrrVector2DU;
var
  cs, sn: Double;
begin
  Degree := Degree * DEGTORAD;
  cs := Cos(Degree);
  sn := Sin(Degree);

  X := Round(X * cs - Y * sn);
  Y := Round(Y * sn + Y * cs);

  Result := Self;
end;

function TIrrVector2DU.RotateBy(Degree: Double; Center: TIrrVector2DU): TIrrVector2DU;
var
  cs, sn: Double;
begin
  Degree := Degree * DEGTORAD;
  cs := Cos(Degree);
  sn := Sin(Degree);

  X := X - Center.X;
  Y := Y - Center.Y;

  X := Round(X * cs - Y * sn);
  Y := Round(X * sn + Y * cs);

  Result := Self;
end;

function TIrrVector2DU.SquaredDistanceFrom(Value: TIrrVector2DU): Single;
begin
  Result := (Self - Value).GetSquaredLength;
end;

function TIrrVector2DU.GetSquaredLength: Single;
begin
  Result := X * X + Y * Y;
end;

class operator TIrrVector2DU.Subtract(Value1, Value2: TIrrVector2DU): TIrrVector2DU;
begin
  Result.X := Value1.X - Value2.X;
  Result.Y := Value1.Y - Value2.Y;
end;

end.